%define parse.error verbose
%locations
%{
#include<bits/stdc++.h>
#include"def.h"
#include"driver.h"
using namespace std;
extern int yylineno;
extern char *yytext;
extern FILE *yyin;
extern int yylex();
void yyerror(const char* fmt, ...);
char *file_name;
string filename;
Node *root=NULL;
%}

%union {
	struct node *ptr;
};

//  %type 定义非终结符的语义值类型
%type  <ptr> CompUnit Decl ConstDecl BType ConstDef ConstInitVal VarDecl VarDef InitVal FuncTitle FuncDef FuncFParams FuncFParam Block BlockItem Stmt Exp Cond LVal PrimaryExp Number UnaryExp UnaryOp FuncRParams MulExp AddExp RelExp EqExp LAndExp LOrExp ConstExp ForExp
// temp non-terminal
%type <ptr> ConstDefCommaList ConstExpBList ConstInitValCommaList ValDefCommaList InitValCommaList FuncFParamsCommaList ExpBList BlockItems ExpCommaList CompUnitOpt ConstInitValOpt InitValOpt AssignOpt FuncFParamsOpt LB_RB_ExpBListOpt Else_StmtOpt ExpOpt FuncRParamsOpt Decl_FuncDef Mul_Div_Mod Add_Minus Lt_Gt_Le_Ge Eq_Ne M N

//% token 定义终结符的语义值类型
%token <ptr>INT_VAL
%token <ptr>FLOAT_VAL
%token <ptr>ID

// ( ) [ ] {  } , ; ! =
%token LP RP LB RB LC RC COMMA SEMICOLON NOT ASSIGN
// + - * / % && || == != < <= > >=
%token ADD MINUS MUL DIV MOD AND OR EQ NE LT LE GT GE
// ++ --
%token INCREASE DECREASE
// type
%token INT FLOAT VOID
// keyword
%token CONST RETURN IF ELSE WHILE BREAK CONTINUE FOR DO
%token LEX_ERR

%left ADD MINUS MUL DIV MOD AND OR EQ NE LT LE GT GE
%right NOT

%nonassoc ELSE
%start CompUnit

%%
CompUnit : CompUnitOpt Decl_FuncDef { 
		$$=new Node(-1,SYM_COMPUNIT); 
		$$->push({$1,$2}); 
		root=$$;
	}
  ;
Decl : ConstDecl {   $$=$1;   }
  |    VarDecl   {   $$=$1;   }
  ;
ConstDecl : CONST BType ConstDef ConstDefCommaList SEMICOLON {
		$$=new Node(0,SYM_CONSTDECL);   
		$$->push({$2,$3,$4}); 
	}
  ;
BType : INT   {  }
  |     FLOAT {  }
  |     VOID  {  }
  ;
ConstDef : ID ConstExpBList ASSIGN ConstInitVal {
		$$=new Node(5,SYM_CONSTDEF); 
		$$->push({$1,$2,$4});
		$$->dimension=$2->children.size();
	}
  ;
ConstInitVal : ConstExp {
		$$=new Node(0,SYM_CONSTINITVAL);  
		$$->push({$1});
	}
  | LC ConstInitValOpt RC {$$=new Node(0,SYM_CONSTINITVAL);  $$->push({$2});}
  ;
VarDecl : BType VarDef ValDefCommaList SEMICOLON {$$=new Node(0,SYM_VARDECL);  $$->push({$1,$2,$3});  }
  ;
VarDef : ID ConstExpBList AssignOpt{$$=new Node(5,SYM_VARDEF);  $$->push({$1,$2,$3}); $$->dimension=$2->children.size();}
  ;
InitVal : Exp {$$=new Node(0,SYM_INITVAL);  $$->push({$1});  }
  | LC InitValOpt RC {$$=new Node(0,SYM_INITVAL);  $$->push({$2});  }
  ;
FuncTitle:BType ID {$$=new Node(4,SYM_EMPTY); $$->push({$1,$2}); $$->lineno = $2->lineno;}
  ;

FuncDef : FuncTitle LP FuncFParamsOpt RP Block {$$=new Node(0,SYM_FUNCDEF);  $$->push({$1,$3,$5}); $$->lineno = $1->lineno;}
  ;
FuncFParams : FuncFParam FuncFParamsCommaList {$$=new Node(0,SYM_FUNCFPARAMS);  $$->push({$1,$2});}
  ;
FuncFParam : BType ID LB_RB_ExpBListOpt {
	$$=new Node(0,SYM_PARAM);
	$$->push({$1,$2,$3}); 
	$$->dimension = $3->children.size();
  }
  ;
Block : LC BlockItems RC SemicolonOpt {$$=new Node(0,SYM_BLOCK);  $$->push({$2});}
  ;
BlockItem : Decl {$$=$1;  }
  | Stmt {$$=$1;  }
  ;
Stmt : LVal ASSIGN Exp SEMICOLON {
	$$=new Node(0,SYM_ASSIGN);
	$$->push({$1,$3});
	}
  | ExpOpt SEMICOLON {
  	$$=$1;  
  	}
  | Block {
  	$$=$1;  
  	}
  | IF LP Cond RP Stmt Else_StmtOpt {
	$$=new Node(5,SYM_BRANCH);
  	Node* ifbranch=new Node(5,SYM_IF); 
  	ifbranch->push({$3,$5}); 
  	$$->push({ifbranch,$6});
  	}
  | WHILE LP Cond RP Stmt {
  	$$=new Node(5,SYM_WHILE);
  	$$->push({$3,$5});  
  	}
  | FOR LP ForExp SEMICOLON Cond SEMICOLON ForExp RP Stmt {
  	$$=new Node(5,SYM_FOR);
  	Node* forexp=new Node(5,SYM_EMPTY);
  	forexp->push({$3,$5,$7});  
  	$$->push({forexp,$9});  
  	}
  | M BREAK N SEMICOLON {
  	$$=new Node(0,SYM_BREAK);  
  	}
  | CONTINUE SEMICOLON {
  	$$=new Node(0,SYM_CONTINUE);  
  	}
  | RETURN ExpOpt SEMICOLON {
  	$$=new Node(5,SYM_RETURN);  
  	$$->push({$2});
  	}
  ;
Exp : AddExp {$$=$1;  }
  ;
Cond : LOrExp {$$=new Node(5,SYM_COND);$$->push({$1});  }
  ;
LVal : ID ExpBList {$$=$1;$$->dimension=$2->children.size(); $$->push({$2}); }
  ;
PrimaryExp : LP Exp RP {$$=$2;  }
  | LVal {$$=$1;  }
  | LVal INCREASE {$$=$1; Node* self_op=new Node(0,SYM_EMPTY);  $$->push({self_op});}
  | LVal DECREASE {$$=$1; Node* self_op=new Node(0,SYM_EMPTY);  $$->push({self_op});  }
  | INCREASE LVal {$$=new Node(5,SYM_EMPTY);  $$->push({$2});  }
  | DECREASE LVal {$$=new Node(5,SYM_EMPTY);  $$->push({$2});  }
  | Number {$$=$1;  }
  ;
Number : INT_VAL {$$=$1; }
  | FLOAT_VAL {$$=$1;  }
  ;
UnaryExp : PrimaryExp {$$=$1;  }
  | ID LP FuncRParamsOpt RP {$$=new Node(3,SYM_FUNC);$$->type_val=$1->type_val;$$->lineno=$1->lineno; $$->push({$3});}
  | UnaryOp UnaryExp {$$=$1;  $$->push({$2});}
  ;
UnaryOp : ADD {$$=new Node(-1,SYM_ADD);  }
  | MINUS {$$=new Node(0,SYM_NEGATIVE);  }
  | NOT {$$=new Node(0,SYM_NOT);  }
  ;
FuncRParams : Exp ExpCommaList {$$=new Node(-1,SYM_LIST); $$->push({$1,$2}); }
  ;
MulExp : UnaryExp {$$=$1;  }
  | MulExp Mul_Div_Mod UnaryExp {$$=$2; $$->push({$1,$3});  }
  ;
AddExp : MulExp {$$=$1;  }
  | AddExp Add_Minus MulExp {$$=$2; $$->push({$1,$3});  }
  ;
RelExp : AddExp {$$=$1;  }
  | RelExp Lt_Gt_Le_Ge AddExp {$$=$2; $$->push({$1,$3});    }
  ;
EqExp : RelExp {$$=$1;  }
  | EqExp Eq_Ne RelExp {$$=$2; $$->push({$1,$3});  }
  ;
LAndExp : EqExp {$$=$1;  }
  | LAndExp AND EqExp {$$=new Node(0,SYM_AND); $$->push({$1,$3});  }
  ;
LOrExp : LAndExp {$$=$1;  }
  | LOrExp OR LAndExp {$$=new Node(0,SYM_OR);  $$->push({$1,$3});  }
  ;
ForExp : %empty {$$=new Node(5, SYM_EMPTY);  }
  | LVal ASSIGN Exp {
	$$=new Node(5, SYM_EXP); 
	Node* assignExp=new Node(0,SYM_ASSIGN);
	assignExp->push({$1,$3}); 
	$$->push({assignExp});
	}
  | Exp {$$=new Node(5,SYM_EXP); $$->push({$1}); }
  ;
ConstExp : AddExp {$$=new Node(-1,SYM_CONSTEXP); $$->push({$1});}
  ;
ConstDefCommaList :%empty {$$=new Node(-1,SYM_LIST);  }
  | ConstDefCommaList COMMA ConstDef {$$=new Node(-1,SYM_LIST); $$->push({$1,$3});  }
  ;
ConstExpBList :%empty {$$=new Node(-1,SYM_LIST);  }
  | ConstExpBList LB ConstExp RB {
	$$=new Node(-1,SYM_LIST);
	$3->tag=5;
	$3->nodeType=SYM_BRAKET;  
	$$->push({$1,$3});  
	}
  ;
ConstInitValCommaList :%empty 		{$$=new Node(-1,SYM_LIST);  }
  | ConstInitValCommaList COMMA ConstInitVal 	{$$=new Node(-1,SYM_LIST);   $$->push({$1,$3});}
  ;
ValDefCommaList :%empty {$$=new Node(-1,SYM_LIST);  }
  | ValDefCommaList COMMA VarDef {$$=new Node(-1,SYM_LIST);   $$->push({$1,$3});  }
  ;
InitValCommaList :%empty {$$=new Node(-1,SYM_LIST);  }
  | InitValCommaList COMMA InitVal {$$=new Node(-1,SYM_LIST);   $$->push({$1,$3});  }
  ;
FuncFParamsCommaList :%empty {$$=new Node(-1,SYM_LIST);  }
  | FuncFParamsCommaList COMMA FuncFParam {$$=new Node(-1,SYM_LIST); $$->push({$1,$3}); }
  ;
ExpBList : %empty {$$=new Node(-1,SYM_LIST);  }
  | ExpBList LB Exp RB {
  	$$=new Node(-1,SYM_LIST);
  	node* bexp=new node(5,SYM_BRAKET);
  	bexp->push({$3});
  	$$->push({$1,bexp});   
  }
  ;
BlockItems : %empty {$$=new Node(-1,SYM_LIST);  }
  | BlockItems BlockItem {$$=new Node(-1,SYM_LIST); $$->push({$1});$$->push({$2});}
  ;
ExpCommaList : %empty {$$=new Node(-1,SYM_LIST);  }
  | ExpCommaList COMMA Exp {$$=new Node(-1,SYM_LIST); $$->push({$1,$3}); }
  ;
SemicolonOpt : %empty { }
  | SEMICOLON { }
  ;
AssignOpt : %empty {$$=new Node(-1,SYM_EMPTY);  }
  | ASSIGN InitVal {$$=$2;  }
  ;
CompUnitOpt : %empty {$$=new Node(-1,SYM_EMPTY);  }
  |  CompUnit {$$=$1;  }
  ;
ConstInitValOpt : %empty {$$=new Node(-1,SYM_EMPTY);  }
  |  ConstInitVal ConstInitValCommaList {$$=new Node(-1,SYM_EMPTY);  $$->push({$1,$2});}
  ;
InitValOpt : %empty {$$=new Node(-1,SYM_EMPTY);  }
  |  InitVal InitValCommaList {$$=new Node(-1,SYM_EMPTY);  $$->push({$1,$2});  }
  ;
FuncFParamsOpt : %empty {$$=new Node(-1,SYM_EMPTY);  }
  | VOID {$$=new Node(-1,SYM_EMPTY);  }
  | FuncFParams {$$=new Node(-1,SYM_EMPTY);  $$->push({$1});  }
  ;
LB_RB_ExpBListOpt : %empty {$$=new Node(-1,SYM_EMPTY);  }
  |  LB RB ExpBList {
  	$$=new Node(-1,SYM_EMPTY);
  	node* b=new node(5,SYM_BRAKET);
   	$$->push({b,$3});  
   }
  ;
Else_StmtOpt : %empty {$$=new Node(-1,SYM_EMPTY);   }
  |  ELSE Stmt {$$=new Node(5,SYM_ELSE);  $$->push({$2});}
  ;
ExpOpt : %empty {$$=new Node(-1,SYM_EMPTY);  }
  | Exp {$$=$1;  }
  ;
FuncRParamsOpt : %empty {$$=new Node(-1,SYM_EMPTY);  }
  | VOID {$$=new Node(-1,SYM_EMPTY);  }
  | FuncRParams {$$=new Node(-1,SYM_EMPTY); $$->push({$1}); }
  ;
Decl_FuncDef :  Decl {$$=new Node(-1,SYM_EMPTY); $$->push({$1});   }
  | FuncDef { $$=new Node(-1,SYM_EMPTY); $$->push({$1});  }
  ;
Mul_Div_Mod : MUL 	{$$=new Node(0,SYM_MUL);  }
  |           DIV 	{$$=new Node(0,SYM_DIV);  }
  |           MOD 	{$$=new Node(0,SYM_MOD);  }
  ;
Add_Minus :   ADD 	{$$=new Node(0,SYM_ADD);  }
  |           MINUS 	{$$=new Node(0,SYM_MINUS);  }
  ;
Lt_Gt_Le_Ge : LT 	{$$=new Node(0,SYM_LESS);  }
  |           GT 	{$$=new Node(0,SYM_GRATER);  }
  |           LE 	{$$=new Node(0,SYM_NOTGRATER); }
  |           GE 	{$$=new Node(0,SYM_NOTLESS); }
  ;
Eq_Ne :       EQ 	{$$=new Node(0,SYM_EQUAL); }
  |           NE 	{$$=new Node(0,SYM_NOTEQUAL); }
  ;
M :           %empty   {$$=new Node(0,SYM_M); }
  ;
N :           %empty   {$$=new Node(0,SYM_N); }
  ;
%%

int main(int argc, char *argv[]) {
#ifdef GETCODE
  FILE *fp = fopen(argv[1],"r");
  char buf[128];
  while(fgets(buf,128,fp))fprintf(stderr,"%s",buf);
  fclose(fp);
#endif
  
  yyin=fopen(argv[1],"r");
  file_name = argv[1];
  char* fstart=file_name;
  while(*file_name)file_name++;
  while(file_name!=fstart&&*(file_name-1)!='/')file_name--;
  filename = string(file_name);
  if (!yyin) return 0;
  yylineno=1;
  yyparse();
  myParser();
  return 0;
}

#include<stdarg.h>
void yyerror(const char* fmt, ...)
{
  va_list ap;
  va_start(ap, fmt);
  fprintf(stderr, "%s:%d\n", file_name, yylineno);
  vfprintf(stderr, fmt, ap);
  fprintf(stderr, ".\n");
}
